home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _4161596A6D334BCD8A6DCBCB77E7CD0E < prev    next >
Encoding:
Text File  |  2002-07-02  |  34.4 KB  |  1,468 lines

  1. // Copyright (C) 2001-2002 Raven Software
  2. //
  3. // g_active.c --
  4.  
  5. #include "g_local.h"
  6.  
  7.  
  8. void P_SetTwitchInfo(gclient_t    *client)
  9. {
  10.     client->ps.painTime = level.time;
  11.     client->ps.painDirection ^= 1;
  12. }
  13.  
  14. /*
  15. ===============
  16. G_DamageFeedback
  17.  
  18. Called just before a snapshot is sent to the given player.
  19. Totals up all damage and generates both the player_state_t
  20. damage values to that client for pain blends and kicks, and
  21. global pain sound events for all clients.
  22. ===============
  23. */
  24. void P_DamageFeedback( gentity_t *player ) 
  25. {
  26.     gclient_t    *client;
  27.     float        count;
  28.     vec3_t        angles;
  29.  
  30.     client = player->client;
  31.     if ( client->ps.pm_type == PM_DEAD ) 
  32.     {
  33.         return;
  34.     }
  35.  
  36.     // total points of damage shot at the player this frame
  37.     count = client->damage_blood + client->damage_armor;
  38.     if ( count == 0 ) 
  39.     {
  40.         // didn't take any damage
  41.         return;        
  42.     }
  43.  
  44.     if ( count > 255 ) 
  45.     {
  46.         count = 255;
  47.     }
  48.  
  49.     // send the information to the client
  50.  
  51.     // world damage (falling, slime, etc) uses a special code
  52.     // to make the blend blob centered instead of positional
  53.     if ( client->damage_fromWorld ) 
  54.     {
  55.         client->ps.damagePitch = 255;
  56.         client->ps.damageYaw = 255;
  57.  
  58.         client->damage_fromWorld = qfalse;
  59.     } 
  60.     else 
  61.     {
  62.         vectoangles( client->damage_from, angles );
  63.         client->ps.damagePitch = angles[PITCH]/360.0 * 255;
  64.         client->ps.damageYaw = angles[YAW]/360.0 * 255;
  65.     }
  66.  
  67.     // play an apropriate pain sound
  68.     if ( (level.time > player->pain_debounce_time)) 
  69.     {
  70.         // don't do more than two pain sounds a second
  71.         if ( level.time - client->ps.painTime < 500 ) 
  72.         {
  73.             return;
  74.         }
  75.  
  76.         P_SetTwitchInfo(client);
  77.         player->pain_debounce_time = level.time + 700;
  78.         G_AddEvent( player, EV_PAIN, player->health );
  79.         client->ps.damageEvent++;
  80.     }
  81.  
  82.  
  83.     client->ps.damageCount = count;
  84.  
  85.     // clear totals
  86.     client->damage_blood = 0;
  87.     client->damage_armor = 0;
  88.     client->damage_knockback = 0;
  89. }
  90.  
  91. /*
  92. =============
  93. P_WorldEffects
  94.  
  95. Check for drowning
  96. =============
  97. */
  98. void P_WorldEffects( gentity_t *ent ) 
  99. {
  100.     int     waterlevel;
  101.  
  102.     if ( ent->client->noclip ) 
  103.     {
  104.         // don't need air
  105.         ent->client->airOutTime = level.time + 12000;    
  106.         return;
  107.     }
  108.  
  109.     waterlevel = ent->waterlevel;
  110.  
  111.     // check for drowning
  112.     if ( waterlevel == 3 && (ent->watertype & CONTENTS_WATER)) 
  113.     {
  114.         // if out of air, start drowning
  115.         if ( ent->client->airOutTime < level.time) 
  116.         {
  117.             // drown!
  118.             ent->client->airOutTime += 1000;
  119.             if ( ent->health > 0 ) 
  120.             {
  121.                 // take more damage the longer underwater
  122.                 ent->damage += 2;
  123.                 if (ent->damage > 15)
  124.                 {
  125.                     ent->damage = 15;
  126.                 }
  127.  
  128.                 // play a gurp sound instead of a normal pain sound
  129.                 if (ent->health <= ent->damage) 
  130.                 {
  131. //                    G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/pain_death/mullins/drown_dead.wav"));
  132.                 } 
  133.                 else
  134.                 {
  135.                     G_AddEvent ( ent, EV_PAIN_WATER, 0 );
  136.                 }
  137.  
  138.                 // don't play a normal pain sound
  139.                 ent->pain_debounce_time = level.time + 200;
  140.  
  141.                 G_Damage (ent, NULL, NULL, NULL, NULL, ent->damage, DAMAGE_NO_ARMOR, MOD_WATER, HL_NONE );
  142.             }
  143.         }
  144.     } 
  145.     else 
  146.     {
  147.         ent->client->airOutTime = level.time + 12000;
  148.         ent->damage = 2;
  149.     }
  150. }
  151.  
  152. /*
  153. ===============
  154. G_SetClientSound
  155. ===============
  156. */
  157. void G_SetClientSound( gentity_t *ent ) 
  158. {
  159. //    ent->client->ps.loopSound = 0;
  160. }
  161.  
  162. /*
  163. ==============
  164. ClientImpacts
  165. ==============
  166. */
  167. void ClientImpacts( gentity_t *ent, pmove_t *pm ) {
  168.     int        i, j;
  169.     trace_t    trace;
  170.     gentity_t    *other;
  171.  
  172.     memset( &trace, 0, sizeof( trace ) );
  173.     for (i=0 ; i<pm->numtouch ; i++) {
  174.         for (j=0 ; j<i ; j++) {
  175.             if (pm->touchents[j] == pm->touchents[i] ) {
  176.                 break;
  177.             }
  178.         }
  179.         if (j != i) {
  180.             continue;    // duplicated
  181.         }
  182.         other = &g_entities[ pm->touchents[i] ];
  183.  
  184.         if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
  185.             ent->touch( ent, other, &trace );
  186.         }
  187.  
  188.         if ( !other->touch ) {
  189.             continue;
  190.         }
  191.  
  192.         other->touch( other, ent, &trace );
  193.     }
  194.  
  195. }
  196.  
  197. /*
  198. ============
  199. G_IsClientSiameseTwin
  200.  
  201. Checks to see if the two clients should never have been separated at birth
  202. ============
  203. */
  204. static qboolean G_IsClientSiameseTwin ( gentity_t* ent, gentity_t* ent2 )
  205. {
  206.     if ( G_IsClientSpectating ( ent->client ) || G_IsClientDead ( ent->client ) )
  207.     {
  208.         return qfalse;
  209.     }
  210.  
  211.     if ( G_IsClientSpectating ( ent2->client ) || G_IsClientDead ( ent2->client ) )
  212.     {
  213.         return qfalse;
  214.     }
  215.  
  216.     if (ent2->r.currentOrigin[0] + ent2->r.mins[0] > ent->r.currentOrigin[0] + ent->r.maxs[0])
  217.     {
  218.         return qfalse;
  219.     }
  220.  
  221.     if (ent2->r.currentOrigin[1] + ent2->r.mins[1] > ent->r.currentOrigin[1] + ent->r.maxs[1])
  222.     {
  223.         return qfalse;
  224.     }
  225.  
  226.     if (ent2->r.currentOrigin[2] + ent2->r.mins[2] > ent->r.currentOrigin[2] + ent->r.maxs[2])
  227.     {
  228.         return qfalse;
  229.     }
  230.  
  231.     if (ent2->r.currentOrigin[0] + ent2->r.maxs[0] < ent->r.currentOrigin[0] + ent->r.mins[0])
  232.     {
  233.         return qfalse;
  234.     }
  235.  
  236.     if (ent2->r.currentOrigin[1] + ent2->r.maxs[1] < ent->r.currentOrigin[1] + ent->r.mins[1])
  237.     {
  238.         return qfalse;
  239.     }
  240.  
  241.     if (ent2->r.currentOrigin[2] + ent2->r.maxs[2] < ent->r.currentOrigin[2] + ent->r.mins[2])
  242.     {
  243.         return qfalse;
  244.     }
  245.  
  246.     return qtrue;
  247. }
  248.  
  249. /*
  250. ============
  251. G_TouchTriggers
  252.  
  253. Find all trigger entities that ent's current position touches.
  254. Spectators will only interact with teleporters.
  255. ============
  256. */
  257. void G_TouchTriggers( gentity_t *ent ) 
  258. {
  259.     int                i;
  260.     int                num;
  261.     int                touch[MAX_GENTITIES];
  262.     gentity_t        *hit;
  263.     trace_t            trace;
  264.     vec3_t            mins;
  265.     vec3_t            maxs;
  266.     static vec3_t    range = { 20, 20, 40 };
  267.  
  268.     if ( !ent->client ) 
  269.     {
  270.         return;
  271.     }
  272.  
  273.     // dead clients don't activate triggers!
  274.     if ( G_IsClientDead ( ent->client ) ) 
  275.     {
  276.         return;
  277.     }
  278.  
  279.     VectorSubtract( ent->client->ps.origin, range, mins );
  280.     VectorAdd( ent->client->ps.origin, range, maxs );
  281.  
  282.     num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
  283.  
  284.     // can't use ent->r.absmin, because that has a one unit pad
  285.     VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
  286.     VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
  287.  
  288.     // Reset the players can use flag
  289.     ent->client->ps.pm_flags &= ~(PMF_CAN_USE);
  290.     ent->client->useEntity = 0;
  291.     ent->client->ps.loopSound = 0;
  292.     ent->s.modelindex  = 0;
  293.  
  294.     for ( i=0 ; i<num ; i++ ) 
  295.     {
  296.         hit = &g_entities[touch[i]];
  297.  
  298.         // pmove would have to have detected siamese twins first
  299.         if ( hit->client && hit != ent && !hit->client->siameseTwin && (ent->client->ps.pm_flags & PMF_SIAMESETWINS) )
  300.         {
  301.             // See if this client has a twin
  302.             if ( !G_IsClientSiameseTwin ( ent, hit ) )
  303.             {
  304.                 continue;
  305.             }
  306.  
  307.             // About time these twins were separated!!
  308.             ent->client->siameseTwin = hit;
  309.             hit->client->siameseTwin = ent;
  310.         }
  311.  
  312.         if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) 
  313.         {
  314.             continue;
  315.         }
  316.  
  317.         // Look for usable gametype triggers and you cant use when zoomed
  318.         if ( !(ent->client->ps.pm_flags & PMF_ZOOMED ) )
  319.         {
  320.             switch ( hit->s.eType )
  321.             {
  322.                 case ET_GAMETYPE_TRIGGER:
  323.                     if ( hit->use && trap_GT_SendEvent ( GTEV_TRIGGER_CANBEUSED, level.time, hit->health, ent->s.number, ent->client->sess.team, 0, 0 ) )
  324.                     {
  325.                         ent->client->ps.pm_flags |= PMF_CAN_USE;
  326.                         ent->client->ps.stats[STAT_USEICON] = hit->delay;
  327.                         ent->client->ps.stats[STAT_USETIME_MAX] = hit->soundPos1;
  328.  
  329.                         if ( ent->client->ps.stats[STAT_USETIME] )
  330.                         {
  331.                             ent->client->ps.loopSound = hit->soundLoop;
  332.                         }
  333.                         ent->client->useEntity = hit;
  334.                         continue;
  335.                     }
  336.                     break;
  337.                 
  338.                 case ET_ITEM:
  339.                     if ( hit->item->giType == IT_GAMETYPE && trap_GT_SendEvent ( GTEV_ITEM_CANBEUSED, level.time, hit->item->quantity, ent->s.number, ent->client->sess.team, 0, 0 ) )
  340.                     {
  341.                         ent->client->ps.pm_flags |= PMF_CAN_USE;
  342.                         ent->client->ps.stats[STAT_USEICON] = level.gametypeItems[hit->item->giTag].useIcon;
  343.                         ent->client->ps.stats[STAT_USETIME_MAX] = level.gametypeItems[hit->item->giTag].useTime;
  344.  
  345.                         if ( ent->client->ps.stats[STAT_USETIME] )
  346.                         {
  347.                             ent->client->ps.loopSound = level.gametypeItems[hit->item->giTag].useSound;
  348.                         }
  349.                         ent->client->useEntity = hit;
  350.                         continue;
  351.                     }
  352.                     break;
  353.             }
  354.         }
  355.  
  356.         if ( !hit->touch && !ent->touch ) 
  357.         {
  358.             continue;
  359.         }
  360.  
  361.         // ignore most entities if a spectator
  362.         if ( G_IsClientSpectating ( ent->client ) ) 
  363.         {
  364.             if ( hit->s.eType != ET_TELEPORT_TRIGGER &&
  365.                 // this is ugly but adding a new ET_? type will
  366.                 // most likely cause network incompatibilities
  367.                 hit->touch != Touch_DoorTrigger) 
  368.             {
  369.                 continue;
  370.             }
  371.         }
  372.  
  373.         // use seperate code for determining if an item is picked up
  374.         // so you don't have to actually contact its bounding box
  375.         if ( hit->s.eType == ET_ITEM ) 
  376.         {
  377.             if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) 
  378.             {
  379.                 continue;
  380.             }
  381.         } 
  382.         else 
  383.         {
  384.             if ( !trap_EntityContact( mins, maxs, hit ) ) 
  385.             {
  386.                 continue;
  387.             }
  388.         }
  389.  
  390.         memset( &trace, 0, sizeof(trace) );
  391.  
  392.         if ( hit->touch ) 
  393.         {
  394.             hit->touch (hit, ent, &trace);
  395.         }
  396.  
  397.         if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) 
  398.         {
  399.             ent->touch( ent, hit, &trace );
  400.         }
  401.     }
  402.  
  403.     // Dont bother looking for twins again unless pmove says so
  404.     ent->client->ps.pm_flags &= (~PMF_SIAMESETWINS);
  405. }
  406.  
  407.  
  408. /*
  409. ============
  410. G_MoverTouchTriggers
  411.  
  412. Find all trigger entities that ent's current position touches.
  413. Spectators will only interact with teleporters.
  414. ============
  415. */
  416. void G_MoverTouchPushTriggers( gentity_t *ent, vec3_t oldOrg ) 
  417. {
  418.     int            i, num;
  419.     float        step, stepSize, dist;
  420.     int            touch[MAX_GENTITIES];
  421.     gentity_t    *hit;
  422.     trace_t        trace;
  423.     vec3_t        mins, maxs, dir, size, checkSpot;
  424.     const vec3_t    range = { 40, 40, 52 };
  425.  
  426.     // non-moving movers don't hit triggers!
  427.     if ( !VectorLengthSquared( ent->s.pos.trDelta ) ) 
  428.     {
  429.         return;
  430.     }
  431.  
  432.     VectorSubtract( ent->r.mins, ent->r.maxs, size );
  433.     stepSize = VectorLength( size );
  434.     if ( stepSize < 1 )
  435.     {
  436.         stepSize = 1;
  437.     }
  438.  
  439.     VectorSubtract( ent->r.currentOrigin, oldOrg, dir );
  440.     dist = VectorNormalize( dir );
  441.     for ( step = 0; step <= dist; step += stepSize )
  442.     {
  443.         VectorMA( ent->r.currentOrigin, step, dir, checkSpot );
  444.         VectorSubtract( checkSpot, range, mins );
  445.         VectorAdd( checkSpot, range, maxs );
  446.  
  447.         num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
  448.  
  449.         // can't use ent->r.absmin, because that has a one unit pad
  450.         VectorAdd( checkSpot, ent->r.mins, mins );
  451.         VectorAdd( checkSpot, ent->r.maxs, maxs );
  452.  
  453.         for ( i=0 ; i<num ; i++ ) 
  454.         {
  455.             hit = &g_entities[touch[i]];
  456.  
  457.             if ( hit->s.eType != ET_PUSH_TRIGGER )
  458.             {
  459.                 continue;
  460.             }
  461.  
  462.             if ( hit->touch == NULL ) 
  463.             {
  464.                 continue;
  465.             }
  466.  
  467.             if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) 
  468.             {
  469.                 continue;
  470.             }
  471.  
  472.  
  473.             if ( !trap_EntityContact( mins, maxs, hit ) ) 
  474.             {
  475.                 continue;
  476.             }
  477.  
  478.             memset( &trace, 0, sizeof(trace) );
  479.  
  480.             if ( hit->touch != NULL ) 
  481.             {
  482.                 hit->touch(hit, ent, &trace);
  483.             }
  484.         }
  485.     }
  486. }
  487.  
  488. /*
  489. =================
  490. G_UpdatePlayerStateScores
  491.  
  492. Update the scores in the playerstate
  493. =================
  494. */
  495. void G_UpdatePlayerStateScores ( gentity_t* ent )
  496. {
  497.     // set the CS_SCORES1/2 configstrings, which will be visible to everyone
  498.     if ( level.gametypeData->teams ) 
  499.     {
  500.         ent->client->ps.persistant[PERS_RED_SCORE] = level.teamScores[TEAM_RED];
  501.         ent->client->ps.persistant[PERS_BLUE_SCORE] = level.teamScores[TEAM_BLUE];
  502.     } 
  503.     else 
  504.     {
  505.         if ( level.numConnectedClients == 0 ) 
  506.         {
  507.             ent->client->ps.persistant[PERS_RED_SCORE] = 0;
  508.             ent->client->ps.persistant[PERS_BLUE_SCORE] = 0;
  509.         } 
  510.         else if ( level.numConnectedClients == 1 ) 
  511.         {
  512.             ent->client->ps.persistant[PERS_RED_SCORE] = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE];
  513.             ent->client->ps.persistant[PERS_BLUE_SCORE] = 0;
  514.         } 
  515.         else 
  516.         {
  517.             ent->client->ps.persistant[PERS_RED_SCORE] = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE];
  518.             ent->client->ps.persistant[PERS_BLUE_SCORE] = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE];
  519.         }
  520.     }
  521. }
  522.  
  523. /*
  524. =================
  525. SpectatorThink
  526. =================
  527. */
  528. void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) 
  529. {
  530.     pmove_t        pm;
  531.     gclient_t    *client;
  532.  
  533.     client = ent->client;
  534.  
  535.     if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) 
  536.     {
  537.         client->ps.pm_type = PM_SPECTATOR;
  538.         client->ps.speed = 400;    // faster than normal
  539.         client->ps.loopSound = 0;
  540.  
  541.         // set up for pmove
  542.         memset (&pm, 0, sizeof(pm));
  543.         pm.ps = &client->ps;
  544.         pm.cmd = *ucmd;
  545.         pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;    // spectators can fly through bodies
  546.         pm.trace = trap_Trace;
  547.         pm.pointcontents = trap_PointContents;
  548.  
  549.         pm.animations = NULL;
  550.  
  551.         // perform a pmove
  552.         Pmove (&pm);
  553.  
  554.         G_UpdatePlayerStateScores ( ent );
  555.  
  556.         // save results of pmove
  557.         VectorCopy( client->ps.origin, ent->s.origin );
  558.  
  559.         G_TouchTriggers( ent );
  560.         trap_UnlinkEntity( ent );
  561.     }
  562.  
  563.     client->oldbuttons = client->buttons;
  564.     client->buttons = ucmd->buttons;
  565.  
  566.     // attack button cycles through spectators
  567.     if ( client->sess.spectatorState != SPECTATOR_FOLLOW && g_forceFollow.integer )
  568.     {
  569.         Cmd_FollowCycle_f( ent, 1 );
  570.     }
  571.     if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) 
  572.     {
  573.         Cmd_FollowCycle_f( ent, 1 );
  574.     }
  575.     else if ( ( client->buttons & BUTTON_ALT_ATTACK ) && ! ( client->oldbuttons & BUTTON_ALT_ATTACK ) ) 
  576.     {
  577.         Cmd_FollowCycle_f( ent, -1 );
  578.     }
  579.     else if ( !g_forceFollow.integer && ucmd->upmove > 0 && (client->ps.pm_flags & PMF_FOLLOW) )
  580.     {
  581.         G_StopFollowing( ent );
  582.     }
  583. }
  584.  
  585. /*
  586. =================
  587. ClientInactivityTimer
  588.  
  589. Returns qfalse if the client is dropped
  590. =================
  591. */
  592. qboolean ClientInactivityTimer( gclient_t *client ) {
  593.     if ( ! g_inactivity.integer ) {
  594.         // give everyone some time, so if the operator sets g_inactivity during
  595.         // gameplay, everyone isn't kicked
  596.         client->inactivityTime = level.time + 60 * 1000;
  597.         client->inactivityWarning = qfalse;
  598.     } else if ( client->pers.cmd.forwardmove || 
  599.         client->pers.cmd.rightmove || 
  600.         client->pers.cmd.upmove ||
  601.         (client->pers.cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) ) {
  602.         client->inactivityTime = level.time + g_inactivity.integer * 1000;
  603.         client->inactivityWarning = qfalse;
  604.     } else if ( !client->pers.localClient ) {
  605.         if ( level.time > client->inactivityTime ) {
  606.             trap_DropClient( client - level.clients, "Dropped due to inactivity" );
  607.             return qfalse;
  608.         }
  609.         if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) {
  610.             client->inactivityWarning = qtrue;
  611.             trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
  612.         }
  613.     }
  614.     return qtrue;
  615. }
  616.  
  617. /*
  618. ==================
  619. ClientTimerActions
  620.  
  621. Actions that happen once a second
  622. ==================
  623. */
  624. void ClientTimerActions( gentity_t *ent, int msec ) 
  625. {
  626.     gclient_t    *client;
  627.  
  628.     client = ent->client;
  629.  
  630.     // Check so see if the player has moved and if so dont let them change their outfitting
  631.     if ( !client->noOutfittingChange && ((level.time - client->respawnTime) > 3000))
  632.     {
  633.         vec3_t vel;
  634.  
  635.         // Check the horizontal velocity for movement
  636.         VectorCopy ( client->ps.velocity, vel );
  637.         vel[2] = 0;
  638.         if ( VectorLengthSquared ( vel ) > 100 )
  639.         {
  640.             client->noOutfittingChange = qtrue;
  641.         }
  642.     }
  643.  
  644.     // Forgive voice chats
  645.     if ( g_voiceFloodCount.integer && ent->client->voiceFloodCount )
  646.     {
  647.         int forgiveTime = 60000 / g_voiceFloodCount.integer;
  648.  
  649.         client->voiceFloodTimer += msec;
  650.         while ( client->voiceFloodTimer >= forgiveTime ) 
  651.         {
  652.             // Forgive one voice chat
  653.             client->voiceFloodCount--;
  654.  
  655.             client->voiceFloodTimer -= forgiveTime;
  656.         }
  657.     }
  658. }
  659.  
  660. /*
  661. ====================
  662. ClientIntermissionThink
  663. ====================
  664. */
  665. void ClientIntermissionThink( gclient_t *client ) 
  666. {
  667.     G_UpdatePlayerStateScores ( &g_entities[client->ps.clientNum] );
  668.  
  669.     client->ps.loopSound = 0;
  670.  
  671.     client->ps.eFlags &= ~EF_TALK;
  672.     client->ps.eFlags &= ~EF_FIRING;
  673.  
  674.     // the level will exit when everyone wants to or after timeouts
  675.  
  676.     // swap and latch button actions
  677.     client->oldbuttons = client->buttons;
  678.     client->buttons = client->pers.cmd.buttons;
  679.  
  680.     if ( (client->buttons & BUTTON_ATTACK) & ( client->oldbuttons ^ client->buttons ) ) 
  681.     {
  682.         // this used to be an ^1 but once a player says ready, it should stick
  683.         client->readyToExit = 1;
  684.     }
  685. }
  686.  
  687. /*
  688. ====================
  689. G_Use
  690.  
  691. use key pressed
  692. ====================
  693. */
  694. void G_Use ( gentity_t* ent )
  695. {
  696.     if ( !ent->client->useEntity )
  697.     {
  698.         return;
  699.     }
  700.  
  701.     if ( ent->client->useEntity->s.eType == ET_ITEM )
  702.     {
  703.         // Make sure one last time that it can still be used
  704.         if ( !trap_GT_SendEvent ( GTEV_ITEM_CANBEUSED, level.time, ent->client->useEntity->item->quantity, ent->s.number, ent->client->sess.team, 0, 0 ) )
  705.         {
  706.             return;
  707.         }
  708.  
  709.         gametype_item_use ( ent->client->useEntity, ent );
  710.         return;
  711.     }
  712.  
  713.     // Make double sure it can still be used
  714.     if ( !trap_GT_SendEvent ( GTEV_TRIGGER_CANBEUSED, level.time, ent->client->useEntity->health, ent->s.number, ent->client->sess.team, 0, 0 ) )
  715.     {
  716.         return;
  717.     }
  718.         
  719.     ent->client->useEntity->use ( ent->client->useEntity, ent, ent );
  720. }
  721.  
  722. /*
  723. ================
  724. ClientEvents
  725.  
  726. Events will be passed on to the clients for presentation,
  727. but any server game effects are handled here
  728. ================
  729. */
  730. void ClientEvents( gentity_t *ent, int oldEventSequence ) 
  731. {
  732.     int            i;
  733.     int            event;
  734.     gclient_t    *client;
  735.     vec3_t        dir;
  736.  
  737.     client = ent->client;
  738.  
  739.     if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) 
  740.     {
  741.         oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS;
  742.     }
  743.  
  744.     for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) 
  745.     {
  746.         event = client->ps.events[ i & (MAX_PS_EVENTS-1) ];
  747.  
  748.         switch ( event ) 
  749.         {
  750.             case EV_FALL_MEDIUM:
  751.             case EV_FALL_FAR:
  752.             {
  753.                 int damage;
  754.                 
  755.                 damage  = client->ps.eventParms[ i & (MAX_PS_EVENTS-1) ];
  756.                 damage &= 0x000000ff;
  757.  
  758.                 client->ps.eventParms[ i & (MAX_PS_EVENTS-1) ] = damage;
  759.                             
  760.                 if ( ent->s.eType != ET_PLAYER ) 
  761.                 {
  762.                     break;        // not in the player model
  763.                 }
  764.                 
  765.                 if ( g_dmflags.integer & DF_NO_FALLING ) 
  766.                 {
  767.                     break;
  768.                 }        
  769.             
  770.                 VectorSet (dir, 0, 0, 1);
  771.                 ent->pain_debounce_time = level.time + 200;    // no normal pain sound
  772.                 G_Damage (ent, NULL, NULL, NULL, NULL, damage, DAMAGE_NO_ARMOR, MOD_FALLING, HL_NONE );
  773.                 break;
  774.             }
  775.  
  776.             case EV_FIRE_WEAPON:
  777.                 ent->client->noOutfittingChange = qtrue;
  778.                 ent->client->invulnerableTime = 0;
  779.                 G_FireWeapon( ent, ATTACK_NORMAL );
  780.                 break;
  781.  
  782.             case EV_ALT_FIRE:
  783.                 ent->client->noOutfittingChange = qtrue;
  784.                 ent->client->invulnerableTime = 0;
  785.                 G_FireWeapon( ent, ATTACK_ALTERNATE );
  786.                 break;
  787.  
  788.             case EV_USE:
  789.                 G_Use ( ent );
  790.                 break;
  791.  
  792.             default:
  793.                 break;
  794.         }
  795.     }
  796.  
  797. }
  798.  
  799. /*
  800. ==============
  801. StuckInOtherClient
  802. ==============
  803. */
  804. static int StuckInOtherClient(gentity_t *ent) 
  805. {
  806.     int i;
  807.     gentity_t    *ent2;
  808.  
  809.     ent2 = &g_entities[0];
  810.     for ( i = 0; i < MAX_CLIENTS; i++, ent2++ ) 
  811.     {
  812.         if ( ent2 == ent ) 
  813.         {
  814.             continue;
  815.         }
  816.  
  817.         if ( !ent2->inuse ) 
  818.         {
  819.             continue;
  820.         }
  821.         
  822.         if ( !ent2->client ) 
  823.         {
  824.             continue;
  825.         }
  826.         
  827.         if ( ent2->health <= 0 ) 
  828.         {
  829.             continue;
  830.         }
  831.         
  832.         //
  833.         if (ent2->r.absmin[0] > ent->r.absmax[0])
  834.             continue;    
  835.         if (ent2->r.absmin[1] > ent->r.absmax[1])
  836.             continue;
  837.         if (ent2->r.absmin[2] > ent->r.absmax[2])
  838.             continue;
  839.         if (ent2->r.absmax[0] < ent->r.absmin[0])
  840.             continue;
  841.         if (ent2->r.absmax[1] < ent->r.absmin[1])
  842.             continue;
  843.         if (ent2->r.absmax[2] < ent->r.absmin[2])
  844.             continue;
  845.         return qtrue;
  846.     }
  847.     return qfalse;
  848. }
  849.  
  850. void BotTestSolid(vec3_t origin);
  851.  
  852. /*
  853. ==============
  854. SendPendingPredictableEvents
  855. ==============
  856. */
  857. void SendPendingPredictableEvents( playerState_t *ps ) {
  858.     gentity_t *t;
  859.     int event, seq;
  860.     int extEvent, number;
  861.  
  862.     // if there are still events pending
  863.     if ( ps->entityEventSequence < ps->eventSequence ) {
  864.         // create a temporary entity for this event which is sent to everyone
  865.         // except the client who generated the event
  866.         seq = ps->entityEventSequence & (MAX_PS_EVENTS-1);
  867.         event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
  868.         // set external event to zero before calling BG_PlayerStateToEntityState
  869.         extEvent = ps->externalEvent;
  870.         ps->externalEvent = 0;
  871.         // create temporary entity for event
  872.         t = G_TempEntity( ps->origin, event );
  873.         number = t->s.number;
  874.         BG_PlayerStateToEntityState( ps, &t->s, qtrue );
  875.         t->s.number = number;
  876.         t->s.eType = ET_EVENTS + event;
  877.         t->s.eFlags |= EF_PLAYER_EVENT;
  878.         t->s.otherEntityNum = ps->clientNum;
  879.         // send to everyone except the client who generated the event
  880.         t->r.svFlags |= SVF_NOTSINGLECLIENT;
  881.         t->r.singleClient = ps->clientNum;
  882.         // set back external event
  883.         ps->externalEvent = extEvent;
  884.     }
  885. }
  886.  
  887. /*
  888. ==============
  889. ClientThink
  890.  
  891. This will be called once for each client frame, which will
  892. usually be a couple times for each server frame on fast clients.
  893.  
  894. If "g_synchronousClients 1" is set, this will be called exactly
  895. once for each server frame, which makes for smooth demo recording.
  896. ==============
  897. */
  898. void ClientThink_real( gentity_t *ent ) 
  899. {
  900.     gclient_t    *client;
  901.     pmove_t        pm;
  902.     int            oldEventSequence;
  903.     int            msec;
  904.     usercmd_t    *ucmd;
  905.  
  906.     client = ent->client;
  907.  
  908.     // don't think if the client is not yet connected (and thus not yet spawned in)
  909.     if (client->pers.connected != CON_CONNECTED) 
  910.     {
  911.         return;
  912.     }
  913.  
  914.     // mark the time, so the connection sprite can be removed
  915.     ucmd = &ent->client->pers.cmd;
  916.  
  917.     // sanity check the command time to prevent speedup cheating
  918.     if ( ucmd->serverTime > level.time + 200 ) 
  919.     {
  920.         ucmd->serverTime = level.time + 200;
  921.     }
  922.     
  923.     if ( ucmd->serverTime < level.time - 1000 ) 
  924.     {
  925.         ucmd->serverTime = level.time - 1000;
  926.     } 
  927.     
  928.     msec = ucmd->serverTime - client->ps.commandTime;
  929.     // following others may result in bad times, but we still want
  930.     // to check for follow toggles
  931.     if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) 
  932.     {
  933.         return;
  934.     }
  935.  
  936.     if ( msec > 200 ) 
  937.     {
  938.         msec = 200;
  939.     }
  940.  
  941.     if ( pmove_msec.integer < 8 ) 
  942.     {
  943.         trap_Cvar_Set("pmove_msec", "8");
  944.     }
  945.     else if (pmove_msec.integer > 33) 
  946.     {
  947.         trap_Cvar_Set("pmove_msec", "33");
  948.     }
  949.  
  950.     if ( pmove_fixed.integer || client->pers.pmoveFixed ) 
  951.     {
  952.         ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
  953.     }
  954.  
  955.     //
  956.     // check for exiting intermission
  957.     //
  958.     if ( level.intermissiontime ) 
  959.     {
  960.         ClientIntermissionThink( client );
  961.         return;
  962.     }
  963.  
  964.     // spectators don't do much
  965.     if ( G_IsClientSpectating ( client ) ) 
  966.     {
  967.         if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) 
  968.         {
  969.             return;
  970.         }
  971.         SpectatorThink( ent, ucmd );
  972.         return;
  973.     }
  974.  
  975.     // check for inactivity timer, but never drop the local client of a non-dedicated server
  976.     if ( !ClientInactivityTimer( client ) ) 
  977.     {
  978.         return;
  979.     }
  980.  
  981.     if ( client->noclip ) 
  982.     {
  983.         client->ps.pm_type = PM_NOCLIP;
  984.     } 
  985.     else if ( client->ps.stats[STAT_HEALTH] <= 0 ) 
  986.     {
  987.         client->ps.pm_type = PM_DEAD;
  988.     }
  989.     else 
  990.     {
  991.         client->ps.pm_type = PM_NORMAL;
  992.     }
  993.  
  994.     client->ps.gravity = g_gravity.value;
  995.  
  996.     // set speed
  997.     client->ps.speed = g_speed.value;
  998.  
  999.     // set up for pmove
  1000.     oldEventSequence = client->ps.eventSequence;
  1001.  
  1002.     memset (&pm, 0, sizeof(pm));
  1003.  
  1004.     pm.ps = &client->ps;
  1005.     pm.cmd = *ucmd;
  1006.     if ( pm.ps->pm_type == PM_DEAD ) 
  1007.     {
  1008.         pm.ps->loopSound = 0;
  1009.         pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
  1010.     }
  1011.     else if ( client->siameseTwin ) 
  1012.     {
  1013.         // Make sure we are still stuck, if so, clip through players.
  1014.         if ( G_IsClientSiameseTwin ( ent, client->siameseTwin ) )
  1015.         {
  1016.             pm.tracemask = MASK_PLAYERSOLID & ~(CONTENTS_BODY);
  1017.         }
  1018.         else
  1019.         {
  1020.             // Ok, we arent stuck anymore so we can clear the stuck flag.
  1021.             client->siameseTwin->client->siameseTwin = NULL;
  1022.             client->siameseTwin = NULL;
  1023.  
  1024.             pm.tracemask = MASK_PLAYERSOLID;
  1025.         }
  1026.     }
  1027.     else if ( ent->r.svFlags & SVF_BOT ) 
  1028.     {
  1029.         pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP;
  1030.     }
  1031.     else 
  1032.     {
  1033.         pm.tracemask = MASK_PLAYERSOLID;
  1034.     }
  1035.     pm.trace = trap_Trace;
  1036.     pm.pointcontents = trap_PointContents;
  1037.     pm.debugLevel = g_debugMove.integer;
  1038.     pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;
  1039.  
  1040.     pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
  1041.     pm.pmove_msec = pmove_msec.integer;
  1042.  
  1043.     pm.animations = NULL;
  1044.  
  1045. #if _Debug
  1046.     pm.isClient=0;
  1047. #endif
  1048.  
  1049.     VectorCopy( client->ps.origin, client->oldOrigin );
  1050.  
  1051.     Pmove (&pm);
  1052.  
  1053.     G_UpdatePlayerStateScores ( ent );
  1054.  
  1055.     // save results of pmove
  1056.     if ( ent->client->ps.eventSequence != oldEventSequence ) 
  1057.     {
  1058.         ent->eventTime = level.time;
  1059.     }
  1060.     
  1061.     // See if the invulnerable flag should be removed for this client
  1062.     if ( ent->client->ps.eFlags & EF_INVULNERABLE )
  1063.     {
  1064.         if ( level.time - ent->client->invulnerableTime >= g_respawnInvulnerability.integer * 1000 )
  1065.         {
  1066.             ent->client->ps.eFlags &= ~EF_INVULNERABLE;
  1067.         }
  1068.     }
  1069.  
  1070.     if (g_smoothClients.integer)
  1071.     {
  1072.         BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
  1073.     }
  1074.     else 
  1075.     {
  1076.         BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
  1077.     }
  1078.  
  1079.     SendPendingPredictableEvents( &ent->client->ps );
  1080.  
  1081.     if ( !( ent->client->ps.eFlags & EF_FIRING ) ) 
  1082.     {
  1083.         client->fireHeld = qfalse;        // for grapple
  1084.     }
  1085.  
  1086.     // use the snapped origin for linking so it matches client predicted versions
  1087.     VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
  1088.  
  1089.     VectorCopy (pm.mins, ent->r.mins);
  1090.     VectorCopy (pm.maxs, ent->r.maxs);
  1091.  
  1092.     ent->waterlevel = pm.waterlevel;
  1093.     ent->watertype = pm.watertype;
  1094.  
  1095.     // Need to cache off the firemodes to the persitant data segment so they
  1096.     // are maintained across spectating and respawning
  1097.     memcpy ( ent->client->pers.firemode, ent->client->ps.firemode, sizeof(ent->client->ps.firemode ) );
  1098.  
  1099.     // execute client events
  1100.     ClientEvents( ent, oldEventSequence );
  1101.  
  1102.     // Update the client animation info
  1103.     G_UpdateClientAnimations ( ent );
  1104.  
  1105.     // link entity now, after any personal teleporters have been used
  1106.     trap_LinkEntity (ent);
  1107.     if ( !ent->client->noclip ) 
  1108.     {
  1109.         G_TouchTriggers( ent );
  1110.     }
  1111.  
  1112.     // NOTE: now copy the exact origin over otherwise clients can be snapped into solid
  1113.     VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
  1114.  
  1115.     // Update the clients anti-lag history
  1116.     G_UpdateClientAntiLag ( ent );
  1117.  
  1118.     // touch other objects
  1119.     ClientImpacts( ent, &pm );
  1120.  
  1121.     // save results of triggers and client events
  1122.     if (ent->client->ps.eventSequence != oldEventSequence) 
  1123.     {
  1124.         ent->eventTime = level.time;
  1125.     }
  1126.  
  1127.     // swap and latch button actions
  1128.     client->oldbuttons = client->buttons;
  1129.     client->buttons = ucmd->buttons;
  1130.     client->latched_buttons |= client->buttons & ~client->oldbuttons;
  1131.  
  1132.     // check for respawning
  1133.     if ( client->ps.stats[STAT_HEALTH] <= 0 ) 
  1134.     {            
  1135.         // wait for the attack button to be pressed
  1136.         if ( level.time > client->respawnTime ) 
  1137.         {
  1138.             if ( g_forcerespawn.integer > 0 && 
  1139.                 ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) 
  1140.             {
  1141.                 respawn( ent );
  1142.                 return;
  1143.             }
  1144.  
  1145.             // pressing attack or use is the normal respawn method
  1146.             if ( ucmd->buttons & BUTTON_ATTACK ) 
  1147.             {
  1148.                 respawn( ent );
  1149.             }
  1150.         }
  1151.  
  1152.         return;
  1153.     }
  1154.  
  1155.     // perform once-a-second actions
  1156.     ClientTimerActions( ent, msec );
  1157. }
  1158.  
  1159. /*
  1160. ==================
  1161. G_CheckClientTeamkill
  1162.  
  1163. Checks to see whether or not this client should be booted from the server
  1164. because they killed too many teammates
  1165. ==================
  1166. */
  1167. void G_CheckClientTeamkill ( gentity_t* ent )
  1168. {
  1169.     char userinfo[MAX_INFO_STRING];
  1170.     char *value;
  1171.  
  1172.     if ( !g_teamkillDamageMax.integer || !level.gametypeData->teams || !ent->client->sess.teamkillDamage ) 
  1173.     {
  1174.         return;
  1175.     }
  1176.     // See if they crossed the max team kill damage
  1177.     else if ( ent->client->sess.teamkillDamage < g_teamkillDamageMax.integer )
  1178.     {    
  1179.         // Does the client need forgiving?
  1180.         if ( ent->client->sess.teamkillForgiveTime )
  1181.         {
  1182.             // Are we in a forgiving mood yet?
  1183.             if ( level.time > ent->client->sess.teamkillForgiveTime + 60000 )
  1184.             {                
  1185.                 ent->client->sess.teamkillForgiveTime += 60000;
  1186.                 ent->client->sess.teamkillDamage -= g_teamkillDamageForgive.integer;
  1187.             }
  1188.         }
  1189.  
  1190.         // All forgivin now?
  1191.         if ( ent->client->sess.teamkillDamage <= 0 )
  1192.         {
  1193.             ent->client->sess.teamkillDamage = 0;
  1194.             ent->client->sess.teamkillForgiveTime = 0;
  1195.         }
  1196.  
  1197.         return;
  1198.     }
  1199.  
  1200.     trap_GetUserinfo( ent->s.number, userinfo, sizeof( userinfo ) );
  1201.     value = Info_ValueForKey (userinfo, "ip");
  1202.  
  1203.     G_LogPrintf( "ClientKick: %i %s - auto kick for teamkilling\n", ent->s.number, value );
  1204.  
  1205.     ent->client->sess.teamkillDamage      = 0;
  1206.     ent->client->sess.teamkillForgiveTime = 0;
  1207.  
  1208.     // Keep track of who was autokicked so we can display a list if need be
  1209.     Com_sprintf ( level.autokickedIP[level.autokickedHead], sizeof(level.autokickedIP[0]), value );
  1210.     Com_sprintf ( level.autokickedName[level.autokickedHead], sizeof(level.autokickedName[0]), ent->client->pers.netname );
  1211.     level.autokickedCount++;
  1212.     if ( level.autokickedCount >= MAX_AUTOKICKLIST )
  1213.     {
  1214.         level.autokickedCount = MAX_AUTOKICKLIST;
  1215.     }
  1216.  
  1217.     level.autokickedHead++;
  1218.     if ( level.autokickedHead >= MAX_AUTOKICKLIST )
  1219.     {
  1220.         level.autokickedHead = 0;
  1221.     }
  1222.  
  1223.     // Buh bye
  1224.     if ( g_teamkillBanTime.integer )
  1225.     {
  1226.         trap_SendConsoleCommand( EXEC_INSERT, va("banclient \"%d\" \"%d\" \"team killing\"\n", ent->s.number, g_teamkillBanTime.integer ) );
  1227.     }
  1228.     else
  1229.     {
  1230.         trap_SendConsoleCommand( EXEC_INSERT, va("clientkick \"%d\" \"team killing\"\n", ent->s.number ) );
  1231.     }
  1232. }
  1233.  
  1234. /*
  1235. ==================
  1236. G_CheckClientTimeouts
  1237.  
  1238. Checks whether a client has exceded any timeouts and act accordingly
  1239. ==================
  1240. */
  1241. void G_CheckClientTimeouts ( gentity_t *ent )
  1242. {
  1243.     // Only timeout supported right now is the timeout to spectator mode
  1244.     if ( !g_timeouttospec.integer )
  1245.     {
  1246.         return;
  1247.     }
  1248.  
  1249.     // Can only do spect timeouts on dedicated servers
  1250.     if ( !g_dedicated.integer )
  1251.     {
  1252.         return;
  1253.     }
  1254.  
  1255.     // Already a spectator, no need to boot them to spectator
  1256.     if ( ent->client->sess.team == TEAM_SPECTATOR )
  1257.     {
  1258.         return;
  1259.     }
  1260.  
  1261.     // Need to be connected
  1262.     if ( ent->client->pers.connected != CON_CONNECTED )
  1263.     {
  1264.         return;
  1265.     }
  1266.  
  1267.     // See how long its been since a command was received by the client and if its 
  1268.     // longer than the timeout to spectator then force this client into spectator mode
  1269.     if ( level.time - ent->client->pers.cmd.serverTime > g_timeouttospec.integer * 1000 )
  1270.     {
  1271.         SetTeam ( ent, "spectator", NULL );
  1272.     }
  1273. }
  1274.  
  1275. /*
  1276. ==================
  1277. ClientThink
  1278.  
  1279. A new command has arrived from the client
  1280. ==================
  1281. */
  1282. void ClientThink( int clientNum ) 
  1283. {
  1284.     gentity_t *ent;
  1285.  
  1286.     ent = g_entities + clientNum;
  1287.     trap_GetUsercmd( clientNum, &ent->client->pers.cmd );
  1288.  
  1289.     // mark the time we got info, so we can display the
  1290.     // phone jack if they don't get any for a while
  1291.     ent->client->lastCmdTime = level.time;
  1292.  
  1293.     if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) 
  1294.     {
  1295.         ClientThink_real( ent );
  1296.     }
  1297. }
  1298.  
  1299. /*
  1300. ==================
  1301. G_RunClient
  1302. ==================
  1303. */
  1304. void G_RunClient( gentity_t *ent ) 
  1305. {
  1306.     if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) 
  1307.     {        
  1308.         return;
  1309.     }
  1310.  
  1311.     ent->client->pers.cmd.serverTime = level.time;
  1312.     ClientThink_real( ent );
  1313. }
  1314.  
  1315.  
  1316. /*
  1317. ==================
  1318. SpectatorClientEndFrame
  1319. ==================
  1320. */
  1321. void SpectatorClientEndFrame( gentity_t *ent ) 
  1322. {
  1323.     gclient_t    *cl;
  1324.  
  1325.     // if we are doing a chase cam or a remote view, grab the latest info
  1326.     if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) 
  1327.     {
  1328.         int        clientNum, flags;
  1329.  
  1330.         clientNum = ent->client->sess.spectatorClient;
  1331.  
  1332.         // team follow1 and team follow2 go to whatever clients are playing
  1333.         if ( clientNum == -1 ) 
  1334.         {
  1335.             clientNum = level.follow1;
  1336.         } 
  1337.         else if ( clientNum == -2 ) 
  1338.         {
  1339.             clientNum = level.follow2;
  1340.         }
  1341.         
  1342.         if ( clientNum >= 0 ) 
  1343.         {
  1344.             cl = &level.clients[ clientNum ];
  1345.         
  1346.             if ( cl->pers.connected == CON_CONNECTED && !G_IsClientSpectating ( cl ) ) 
  1347.             {
  1348.                 int count;
  1349.                 int ping;
  1350.                 int score;
  1351.                 int respawnTimer;
  1352.  
  1353.                 count = ent->client->ps.persistant[PERS_SPAWN_COUNT];
  1354.                 ping  = ent->client->ps.ping;
  1355.                 score = ent->client->ps.persistant[PERS_SCORE];
  1356.                 flags = (cl->ps.eFlags & ~(EF_VOTED)) | (ent->client->ps.eFlags & (EF_VOTED));
  1357.                 respawnTimer = ent->client->ps.respawnTimer;
  1358.  
  1359.                 ent->client->ps = cl->ps;
  1360.                 ent->client->ps.pm_flags |= PMF_FOLLOW;
  1361.                 ent->client->ps.eFlags = flags;
  1362.                 ent->client->ps.persistant[PERS_SPAWN_COUNT] = count;
  1363.                 ent->client->ps.persistant[PERS_SCORE] = score;
  1364.                 ent->client->ps.ping = ping;
  1365.                 ent->client->ps.respawnTimer = respawnTimer;
  1366.  
  1367.                 return;
  1368.             } 
  1369.             else 
  1370.             {
  1371.                 // drop them to free spectators unless they are dedicated camera followers
  1372.                 if ( ent->client->sess.spectatorClient >= 0 ) 
  1373.                 {
  1374.                     Cmd_FollowCycle_f (ent, 1);
  1375.                 }
  1376.             }
  1377.         }
  1378.     }
  1379.  
  1380.     if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) 
  1381.     {
  1382.         ent->client->ps.pm_flags |= PMF_SCOREBOARD;
  1383.     } 
  1384.     else 
  1385.     {
  1386.         ent->client->ps.pm_flags &= ~PMF_SCOREBOARD;
  1387.     }
  1388. }
  1389.  
  1390. /*
  1391. ==============
  1392. ClientEndFrame
  1393.  
  1394. Called at the end of each server frame for each connected client
  1395. A fast client will have multiple ClientThink for each ClientEdFrame,
  1396. while a slow client may have multiple ClientEndFrame between ClientThink.
  1397. ==============
  1398. */
  1399. void ClientEndFrame( gentity_t *ent ) 
  1400. {
  1401.     clientPersistant_t    *pers;
  1402.  
  1403.     if ( G_IsClientSpectating ( ent->client ) ) 
  1404.     {
  1405.         SpectatorClientEndFrame( ent );
  1406.         return;
  1407.     }
  1408.  
  1409.     pers = &ent->client->pers;
  1410.  
  1411.     // save network bandwidth
  1412. #if 0
  1413.     if ( !g_synchronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) 
  1414.     {
  1415.         // FIXME: this must change eventually for non-sync demo recording
  1416.         VectorClear( ent->client->ps.viewangles );
  1417.     }
  1418. #endif
  1419.  
  1420.     //
  1421.     // If the end of unit layout is displayed, don't give
  1422.     // the player any normal movement attributes
  1423.     //
  1424.     if ( level.intermissiontime ) 
  1425.     {
  1426.         return;
  1427.     }
  1428.  
  1429.     // burn from lava, etc
  1430.     P_WorldEffects (ent);
  1431.  
  1432.     // apply all the damage taken this frame
  1433.     P_DamageFeedback (ent);
  1434.  
  1435.     // add the EF_CONNECTION flag if we haven't gotten commands recently
  1436.     if ( level.time - ent->client->lastCmdTime > 1000 ) 
  1437.     {
  1438.         ent->s.eFlags |= EF_CONNECTION;
  1439.     } 
  1440.     else 
  1441.     {
  1442.         ent->s.eFlags &= ~EF_CONNECTION;
  1443.     }
  1444.  
  1445.     // FIXME: get rid of ent->health...
  1446.     ent->client->ps.stats[STAT_HEALTH] = ent->health;    
  1447.  
  1448.     G_SetClientSound (ent);
  1449.  
  1450.     // set the latest infor
  1451.     if (g_smoothClients.integer) 
  1452.     {
  1453.         BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
  1454.     }
  1455.     else 
  1456.     {
  1457.         BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
  1458.     }
  1459.     
  1460.     SendPendingPredictableEvents( &ent->client->ps );
  1461.  
  1462.     // set the bit for the reachability area the client is currently in
  1463. //    i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin );
  1464. //    ent->client->areabits[i >> 3] |= 1 << (i & 7);
  1465. }
  1466.  
  1467.  
  1468.